
package com.borqs.music.util;

import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.HashMap;

import org.mozilla.intl.chardet.nsDetector;
import org.mozilla.intl.chardet.nsICharsetDetectionObserver;

import android.os.Environment;
import android.os.Message;

import com.borqs.music.MusicApplication;
import com.borqs.music.executor.MusicExecutor;
import com.borqs.music.executor.Task;
import com.borqs.music.executor.TaskAdapter;
import com.borqs.music.executor.TaskListener;
import com.borqs.music.tasks.AutoQueryLyricTask;

public class AsyncLyricLoader {
    private static final String SDCARD_PATH = Environment.getExternalStorageDirectory().getPath();
    private static final String CACHE_LYRIC_DIR = new File(SDCARD_PATH, "borqs/borqsmusic/lyric")
            .getPath();
    private HashMap<String, SoftReference<String>> mLyricCache = new HashMap<String, SoftReference<String>>();
    private static AsyncLyricLoader mLoaderInstance;

    private AsyncLyricLoader() {
    }

    public static synchronized AsyncLyricLoader getInstance() {
        if (null == mLoaderInstance) {
            mLoaderInstance = new AsyncLyricLoader();
        }
        return mLoaderInstance;
    }

    public void clear() {
        mLyricCache.clear();
    }

    private String generateLyricFileName(String artist, String title,
            String songMd5) {
        return artist + "-" + title + songMd5;
    }

    public String getLyricMD5FromCache(String artist, String title,
            String songMd5) {
        if (mLyricCache.containsKey(songMd5)) {
            String lyric = mLyricCache.get(songMd5).get();
            if (null != lyric) {
                return MD5Utils.getMD5(lyric.getBytes());
            }
        }
        return MusicApplication.MD5_NULL;
    }

    public boolean updateLyricInCache(String artist, String title,
            String songMd5, String lyric) {
        if (lyric != null && lyric.trim().length() > 0) {
            SoftReference<String> softReference = new SoftReference<String>(lyric);
            mLyricCache.put(songMd5, softReference);
            saveLyricToFile(generateLyricFileName(artist, title, songMd5), lyric);
        }
        return true;
    }

    public String loadLyricByUrl(String url, final String songMd5, String title,
            String artist, final Message msg) {
        if (mLyricCache.containsKey(songMd5)) {
            SoftReference<String> softReference = mLyricCache.get(songMd5);
            String lyric = softReference.get();
            if (lyric != null) {
                return lyric;
            }
        }

        final String fileName = generateLyricFileName(artist, title, songMd5);
        String lyric = loadLyricFromLocal(url, fileName);

        if (lyric != null) {
            SoftReference<String> softReference = new SoftReference<String>(lyric);
            mLyricCache.put(songMd5, softReference);
            return lyric;
        }

        TaskListener lyricListener = new TaskAdapter() {
            @Override
            public void onDone(Task task) {
                String lyric = (String) task.getResult();
                saveLyricToFile(fileName, lyric);
                SoftReference<String> softReference = new SoftReference<String>(lyric);
                mLyricCache.put(songMd5, softReference);
                msg.obj = lyric;
                msg.sendToTarget();
            }
        };

        AutoQueryLyricTask task = new AutoQueryLyricTask(lyricListener, title, artist);
        MusicExecutor.executeTask(task);
        return null;
    }

    public String loadLyricByMd5(String ticket, final String songMd5, String title, String artist,
            final Message msg) {
        if (mLyricCache.containsKey(songMd5)) {
            SoftReference<String> softReference = mLyricCache.get(songMd5);
            String lyric = softReference.get();
            if (lyric != null) {
                return lyric;
            }
        }

        final String fileName = generateLyricFileName(artist, title, songMd5);
        String lyric = loadLyricFromLocal(null, fileName);
        if (lyric != null) {
            SoftReference<String> softReference = new SoftReference<String>(lyric);
            mLyricCache.put(songMd5, softReference);
            return lyric;
        }

        TaskListener infoListener = new TaskAdapter() {
            @Override
            public void onDone(Task task) {
                String lyric = (String) task.getResult();
                saveLyricToFile(fileName, lyric);
                SoftReference<String> softReference = new SoftReference<String>(lyric);
                mLyricCache.put(songMd5, softReference);
                // Needn't send message when downloading song and lyric.
                if (null != msg) {
                    msg.obj = lyric;
                    msg.sendToTarget();
                }
            }
        };

        AutoQueryLyricTask task = new AutoQueryLyricTask(infoListener, title, artist);
        task.setTicket(ticket);
        MusicExecutor.executeTask(task);
        return null;
    }

    private static void closeForcibly(Closeable stream) {
        if (stream == null) {
            return;
        }
        try {
            stream.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public static void saveLyricToFile(String fileName, String lyric) {
        if (fileName == null || lyric == null || lyric.length() == 0) {
            return;
        }

        File file = new File(CACHE_LYRIC_DIR);
        if (!file.exists()) {
            file.mkdirs();
        }

        file = new File(CACHE_LYRIC_DIR, fileName + ".lrc");
        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(lyric.getBytes());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static String loadLyricFromLocal(String url, String key) {
        File file = null;
        boolean isFound = false;
        if (url != null && url.startsWith(SDCARD_PATH)) {
            // read the lrc file from the same directory.
            // get lyric file
            int dotIndex = url.lastIndexOf(".");
            String filePath = url.substring(0, dotIndex + 1);

            String lrc = filePath + "lrc";
            file = new File(lrc);
            isFound = file.exists(); // check lrc

            if (!isFound) {
                String txt = filePath + "txt";
                file = new File(txt);
                isFound = file.exists(); // check txt
            }
        }

        if (!isFound && key != null) {
            file = new File(CACHE_LYRIC_DIR, key + ".lrc");
            isFound = file.exists();// check lyric folder
        }

        if (!isFound) {
            return null;
        }

        try {
            final FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int len = -1;
            while ((len = fis.read(buf)) != -1) {
                baos.write(buf, 0, len);
            }

            closeForcibly(fis);
            byte[] data = baos.toByteArray();
            String charset = detectEncoding(data);
            return new String(data, charset);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    public static String loadLyricPath(String url, String fileName) {
        File file = null;
        boolean isFound = false;
        if (url != null && url.startsWith(SDCARD_PATH)) {
            // read the lrc file from the same directory.
            // get lyric file
            int dotIndex = url.lastIndexOf(".");
            String filePath = url.substring(0, dotIndex + 1);

            String lrc = filePath + "lrc";
            file = new File(lrc);
            isFound = file.exists(); // check lrc

            if (!isFound) {
                String txt = filePath + "txt";
                file = new File(txt);
                isFound = file.exists(); // check txt
            }
        }

        if (!isFound && fileName != null) {
            file = new File(CACHE_LYRIC_DIR, fileName + ".lrc");
            isFound = file.exists();// check lyric folder
        }

        if (isFound) {
            return file.getAbsolutePath();
        }

        return null;
    }

    /**
     * Whether lyric and song is same folder or not.
     * 
     * @param song local url
     * @return boolean
     */
    public static boolean isLocalLyric(String url) {
        File file = null;
        boolean isFound = false;
        if (url != null && url.startsWith(SDCARD_PATH)) {
            // read the lrc file from the same directory.
            // get lyric file
            int dotIndex = url.lastIndexOf(".");
            String filePath = url.substring(0, dotIndex + 1);

            String lrc = filePath + "lrc";
            file = new File(lrc);
            isFound = file.exists(); // check lrc

            if (!isFound) {
                String txt = filePath + "txt";
                file = new File(txt);
                isFound = file.exists(); // check txt
            }
        }

        if (isFound) {
            return true;
        }

        return false;
    }

    public static String detectEncoding(byte[] data) {
        String charset = null;
        nsDetector dector = new nsDetector(nsDetector.ALL);
        dector.Init(charsetDetectionObserver);

        if (!dector.isAscii(data, data.length)) {
            dector.DoIt(data, data.length, false);
        } else {
            charset = "iso-8859-1";
        }

        dector.DataEnd();

        if (charset == null) {
            String prob[] = dector.getProbableCharsets();
            if (prob.length > 0) {
                charset = prob[0];
            } else {
                charset = "utf-8";
            }

        }

        // android doesn't support gb18030 yet
        if ("gb18030".equalsIgnoreCase(charset))
            charset = "gbk";

        return charset;
    }

    private static nsICharsetDetectionObserver charsetDetectionObserver = new nsICharsetDetectionObserver() {
        public void Notify(String arg0) {
        }
    };
}
